home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.04 Apr 95 / Performance / Fractal 5 / FractalEngine.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-13  |  13.6 KB  |  585 lines  |  [TEXT/MPS ]

  1. /*    
  2.     File:        FractalEngine.c
  3.  
  4.     Used to build:    “Fractal 5”
  5.     
  6.     Written by:        Jim Cathey            July 1985
  7.                     Eric Traut            November 1994
  8.  
  9.     Description:
  10.         The following code implements a “Fractal Contour” generating
  11.         program. See comments in the file “FractalMain.c” for more
  12.         information.
  13.         
  14.         The code in this file implements the fractal-generating
  15.         and plotting portion of the program.
  16. */
  17.  
  18.  
  19. #include <Quickdraw.h>
  20. #include <ToolUtils.h>
  21.  
  22. #include <stdlib.h>
  23.  
  24. #include "Fractal.h"
  25.  
  26. /* Functions defined within this file */
  27. double RandomDouble(void);
  28. void CalcXs(short len, short incr, short sk);
  29. void CalcYs(short len, short incr, short sk);
  30. void CalcDiagonals(short len, short incr, short sk);
  31. short GetZValue(short x, short y);
  32. void SetZValue(short d, short x, short y);
  33. void SeaLevel(short *newx, short *newy, short *newz);
  34. short ScaleValue(short base, short numer, short denom);
  35. void DoPlot(short xindex, short yindex);
  36. void PlotTo(short x, short y, short z);
  37. void Rotate(short *x, short *y);
  38. void TiltDown(short *x, short *z);
  39. void DrawLine(short x, short y);
  40. void Cordic(short *x, short *y, short scale, short count);
  41. void ScaledMoveTo(short x, short y);
  42. void ScaledLineTo(short x, short y);
  43. void SetLineColor(short colorCode);
  44. void MoveToOffscreen(short x, short y);
  45. void LineToOffscreen(short x, short y);
  46. void LineXOffscreen(short startX, short startY, short endX, short endY);
  47. void LineYOffscreen(short startX, short startY, short endX, short endY);
  48.  
  49. /* Global variables */
  50. short             gMaxX, gMaxY;
  51. short            gCurrentX, gCurrentY;
  52. short            gPixelColor;            /* Pixel value for offscreen line drawing */
  53. Boolean         gAtLineStart;            /* True at the first of the line */
  54. Boolean         gOnLand;                /* True when plotting land, else false */
  55. char*            gPixelBase;                /* Base of pixel store */
  56.  
  57.  
  58. /*
  59.     RandomDouble
  60.     
  61.     This function returns a random number between -1 and 1.
  62. */
  63. double RandomDouble(void)
  64. {
  65.     return (double)rand() / RAND_MAX;
  66. }
  67.  
  68.  
  69. /*
  70.     CalcSurface
  71.     
  72.     This is the main surface-generating routine. It generates a random
  73.     fractal surface by filling in the gPointArray array.
  74. */
  75. void CalcSurface(short level)
  76. {
  77.     short             i, j, length, incrby, sk;
  78.     float             power;
  79.     long            startTicks;
  80.         
  81.     gTotalFractals++;
  82.     gFractalChanged = true;
  83.     startTicks = TickCount();
  84.     
  85.     gMaxX = 1 << level;
  86.     gMaxY = gMaxX / 2;
  87.     for (i = 0; i <= gMaxX; i++)           /* Clear the Array.  Use i & incrby as temps */
  88.     for (incrby = 0; incrby <= gMaxY; incrby++)
  89.         (*gPointArray)[i][incrby] = 0;
  90.  
  91.     for (i = 1; i <= level; i++) {
  92.         for (power = 1.0, j = 0; j < i; j++)
  93.             power *= 1.8;
  94.         length = 10000 / power;            /* = 10000/(1.8^i) */
  95.         incrby = gMaxX / (1 << i);         /* # of line segments in a side of the triangle */
  96.         sk = incrby * 2;
  97.         CalcXs(length, incrby, sk);
  98.         CalcYs(length, incrby, sk);
  99.         CalcDiagonals(length, incrby, sk);
  100.     }
  101.     
  102.     gTotalTickCount += TickCount() - startTicks;
  103. }
  104.  
  105.  
  106. /*
  107.     CalcXs
  108.     
  109.     This function calculates the fractal values in the X direction.
  110.     It is called for each level and subdivides the existing edges
  111.     of the fractal to get the next level.
  112. */
  113. void CalcXs(short len, short incr, short sk)
  114. {
  115.     short             y, x;
  116.     short             d1, d2;
  117.  
  118.     for (y=0; y < gMaxX; y += sk) {
  119.         for (x = incr+y; x <= gMaxX; x += sk) {
  120.             d1 = GetZValue(x-incr, y);
  121.             d2 = GetZValue(x+incr, y);
  122.             SetZValue(((d1 + d2) >> 1) + (short)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  123.         }
  124.     }
  125. }
  126.  
  127.  
  128. /*
  129.     CalcYs
  130.  
  131.     This function calculates the fractal values in the Y direction.
  132.     It is called for each level and subdivides the existing edges
  133.     of the fractal to get the next level.
  134. */
  135. void CalcYs(short len, short incr, short sk)
  136. {
  137.     short y, x;
  138.     short d1, d2;
  139.  
  140.     for (x = gMaxX; x >= 1; x -= sk)
  141.         for (y = incr; y <= x; y += sk) {
  142.             d1 = GetZValue(x, y + incr);
  143.             d2 = GetZValue(x, y - incr);
  144.             SetZValue(((d1 + d2) >> 1) + (short)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  145.         }
  146. }
  147.  
  148.  
  149. /*
  150.     CalcDiagonals
  151.  
  152.     This function calculates the fractal values in the diagonal direction.
  153.     It is called for each level and subdivides the existing edges
  154.     of the fractal to get the next level.
  155. */
  156. void CalcDiagonals(short len, short incr, short sk)
  157. {
  158.     short             y, x;
  159.     short             d1, d2;
  160.         
  161.     for (x = 0; x < gMaxX; x += sk)
  162.     for (y = incr; y <= gMaxX - x; y += sk) {
  163.         d1 = GetZValue(x + y - incr, y - incr);
  164.         d2 = GetZValue(x + y + incr, y + incr);
  165.         SetZValue(((d1 + d2) >> 1) + (short)(RandomDouble() * (len >> 1)) - (len >> 2), x + y, y);
  166.     }
  167. }
  168.  
  169.  
  170. /*
  171.     GetZValue
  172.  
  173.     This function returns the Z value (i.e. the value stored in the
  174.     two-dimensional gPlotArray array. Because the fractal is triangular
  175.     and the array is square, we will store half the triange in the lower
  176.     part of the array the the other half in the upper part of the
  177.     array.
  178. */
  179. short GetZValue(short x, short y)
  180. {
  181.     if (y <= gMaxY)
  182.         return (*gPointArray)[x][y];
  183.     else
  184.         return (*gPointArray)[gMaxX - x][gMaxX + 1 - y];
  185. }
  186.  
  187.  
  188. /*
  189.     SetZValue
  190.  
  191.     This function saves the Z value (i.e. the value in the
  192.     two-dimensional gPlotArray array. Because the fractal is triangular
  193.     and the array is square, we will store half the triange in the lower
  194.     part of the array the the other half in the upper part of the
  195.     array.
  196. */
  197. void SetZValue(short d, short x, short y)
  198. {
  199.     if (y <= gMaxY)
  200.         (*gPointArray)[x][y] = d;
  201.     else
  202.         (*gPointArray)[gMaxX - x][gMaxX + 1 - y] = d;
  203. }
  204.  
  205.  
  206. /*
  207.     PlotData
  208.     
  209.     Our window is already open at this point, and it is cleared.
  210.     All we have to do now is fill it. This function draws the 
  211.     2D projection of the triangular database on the screen.
  212. */
  213. void PlotData(void)
  214. {
  215.     short             xindex, yindex;
  216.  
  217.     gPixelBase = (char*)GetPixBaseAddr(gOffscreenPixMap);
  218.     gOnLand = true;                                        /* On land to start */
  219.  
  220.     for (xindex = 0; xindex <= gMaxX; xindex++) {        /* Plot along X axis */
  221.         gAtLineStart = true;
  222.         for (yindex = 0; yindex <= xindex; yindex++)
  223.             DoPlot(xindex, yindex);
  224.     }
  225.     
  226.     for (yindex = 0; yindex <= gMaxX; yindex++) {        /* Plot along Y axis */
  227.         gAtLineStart = true;
  228.         for (xindex = yindex; xindex <= gMaxX; xindex++)
  229.             DoPlot(xindex, yindex);
  230.     }
  231.     
  232.     for (xindex = 0; xindex <= gMaxX; xindex++) {        /* Plot along the diagonal */
  233.         gAtLineStart = true;
  234.         for (yindex = 0; yindex <= gMaxX - xindex; yindex++)
  235.             DoPlot(xindex + yindex, yindex);
  236.     }
  237. }
  238.  
  239.  
  240. /*
  241.     DoPlot
  242.     
  243.     This function plots a single line or point from the current X and
  244.     Y value to the new X and Y value specified.
  245. */
  246. void DoPlot(short xindex, short yindex)
  247. {
  248.     short        xcoord, ycoord, zcoord;
  249.  
  250.     zcoord = GetZValue(xindex, yindex);
  251.     ycoord = ScaleValue(yindex, 10000, gMaxX);
  252.     xcoord = ScaleValue(xindex, 10000, gMaxX) - ycoord/2;
  253.     if (gContourType == kStyleWater)
  254.         SeaLevel(&xcoord, &ycoord, &zcoord);
  255.     PlotTo(xcoord, ycoord, zcoord);
  256. }
  257.  
  258.  
  259. /*
  260.     SeaLevel
  261.     
  262.     This function is used to plot lines when using the “foothills
  263.     with water” mode. It determines whether a line goes under
  264.     water at any point. If it does, it simply draws a point
  265.     to represent the water.
  266. */
  267. void SeaLevel(short *newX, short *newY, short *newZ)
  268. {
  269.     static short     oldX, oldY, oldZ;            /* The starting point for the next call */
  270.     short             waterX, waterY, waterZ;     /* Where the vector hits the waterline. */
  271.     float             scratch;
  272.  
  273.     if (gAtLineStart) {                        /* If at the beginning of the line */
  274.         if ((oldZ = *newZ) < 0) {            /*  and if we’re underwater */
  275.             SetLineColor(blueColor);
  276.             gOnLand = false;
  277.             *newZ = 0;                        /* Clip to the waterline */
  278.         }
  279.         else {
  280.             SetLineColor(blackColor);
  281.             gOnLand = true;                    /* Otherwise we’re on land from the start */
  282.         }
  283.     }
  284.     else {                                    /* Else we’re in the middle of a line and */
  285.         if (oldZ > 0 && *newZ > 0) {        /* Start & end points both above water.. */
  286.             oldZ = *newZ;
  287.         }
  288.         else if (oldZ < 0 && *newZ < 0) {    /* Start & end points both under water... */
  289.             oldZ = *newZ;
  290.             *newZ = 0;                        /* Clip at the waterline */
  291.         }
  292.         else {                                /* We’re now crossing the waterline, so calculate */
  293.                                             /*  the exact point where it dives under */
  294.             scratch = (float) (*newZ) / (*newZ - oldZ);                /* Proportion of the line that’s */
  295.             waterX = (short) ((oldX - *newX) * scratch) + *newX;    /*  below the water */
  296.             waterY = (short) ((oldY - *newY) * scratch) + *newY;
  297.             waterZ = 0;
  298.  
  299.             PlotTo(waterX, waterY, waterZ);    /* Draw to the waterline first */
  300.             
  301.             /* The plot from the waterline to the endpoint in the new color is done elsewhere */
  302.             if (*newZ > 0) {                /* Emerging from the water */
  303.                 SetLineColor(blackColor);
  304.                 gOnLand = true;
  305.                 oldZ = *newZ;
  306.             }
  307.             else {                            /* Diving into the water. */
  308.                 SetLineColor(blueColor);
  309.                 gOnLand = false;
  310.                 oldZ = *newZ;
  311.                 *newZ = 0;
  312.             }
  313.         }
  314.     }
  315.     oldX = *newX;                            /* Save the real endpoint of the vector */
  316.     oldY = *newY;                            /*  to use as the start of the next call */
  317. }
  318.  
  319.  
  320. /*
  321.     ScaleValue
  322.     
  323.     Computes base * numer / denom with long intermediate
  324. */
  325. short ScaleValue(short base, short numer, short denom)
  326. {
  327.     long            temp;
  328.  
  329.     temp = (long) base * (long) numer;
  330.     temp /= (long) denom;
  331.     return (short) temp;
  332. }
  333.  
  334.  
  335. /*
  336.     PlotTo
  337.     
  338.     This function converts a 3-D line to a 2-D line and plots it
  339. */
  340. void PlotTo(short x, short y, short z)
  341. {
  342.     Rotate(&x, &y);         /* Rotate 30 deg. towards Y in the XY plane */
  343.     TiltDown(&x, &z);       /* Tip 36 deg. down in the ZX plane */
  344.     x /= 25;                /* Scale 10K to 400. */
  345.     y /= 25;
  346.     z /= 25;
  347.     DrawLine(y, z);         /* Show the YZ planar projection. */
  348. }
  349.  
  350.  
  351. /*
  352.     Rotate
  353.     
  354.     This function rotates a point in the XY plane a + 30 degress.
  355. */ 
  356. void Rotate(short *x, short *y)
  357. {
  358.     Cordic(x, y, 5, 17);
  359. }
  360.  
  361.  
  362. /*
  363.     TiltDown
  364.     
  365.     This function rotates a point in the XZ plane a + or - 36 degress.
  366. */
  367. void TiltDown(short *x, short *z)
  368. {
  369.     Cordic(x, z, 5, (gContourType == kStyleMountains) ? 20 : -20);
  370. }
  371.  
  372.  
  373. /*
  374.     DrawLine
  375.     
  376.     This function draws a line from the last endpoint
  377. */
  378. void DrawLine(short x, short y)
  379. {
  380.     x += x/10 + 10;                         /* Compute x1.1 + tiny offset */
  381.     if (gContourType == kStyleMountains)
  382.             y = 220 - y;                    /* Move the baseline for mountains */
  383.     else
  384.             y = 80 - y;
  385.  
  386.     if (gAtLineStart || !gOnLand)            /* Only a point then */
  387.         ScaledMoveTo(x, y);
  388.  
  389.     ScaledLineTo(x, y);
  390.     gAtLineStart = false;
  391. }
  392.  
  393.  
  394. /*
  395.     Cordic
  396.     
  397.     This function spins the XY vector ‘count’ steps to the left 
  398.     using a CORDIC algorithm with a shift factor of ‘scale.’
  399.     Rotates atan(1/(2^scale)) degrees/step (e.g. scale of 5 is 
  400.     1.79 deg/step;  4 = 3.57 d/s...)
  401.     
  402.     *x and *y should be large for accuracy.
  403. */
  404. void Cordic(short *x, short *y, short scale, short count)      
  405. {
  406.     short            tempX, tempY;
  407.  
  408.     tempX = *x;
  409.     tempY = *y;
  410.     if (count > 0)                        /* Positive count is CCW (left) */
  411.         for (; count; count--) {
  412.             tempX -= (tempY >> scale);
  413.             tempY += (tempX >> scale);
  414.         }
  415.     else                                /* Negative is CW (right) */
  416.         for (; count; count++) {
  417.             tempX += (tempY >> scale);
  418.             tempY -= (tempX >> scale);
  419.         }
  420.     *x = tempX;
  421.     *y = tempY;
  422. }
  423.  
  424.  
  425. /*
  426.     ScaledMoveTo
  427.     
  428.     This function performs a scaled MoveTo by scaling the given
  429.     point from MacPlus screen coordinates to the size of the
  430.     current main window.
  431. */
  432. void ScaledMoveTo(short x, short y)
  433. {
  434.     short scaledX, scaledY;
  435.     
  436.     scaledX = (long)x * (long)kNewScreenX / kOriginalScreenX;
  437.     scaledY = (long)y * (long)kNewScreenY / kOriginalScreenY;
  438.  
  439.     MoveToOffscreen(scaledX, scaledY);
  440. }
  441.  
  442.  
  443. /*
  444.     ScaledLineTo
  445.  
  446.     This function performs a scaled LineTo by scaling the given
  447.     point from MacPlus screen coordinates to the size of the
  448.     current main window.
  449. */
  450. void ScaledLineTo(short x, short y)
  451. {
  452.     short scaledX, scaledY;
  453.  
  454.     scaledX = (long)x * (long)kNewScreenX / kOriginalScreenX;
  455.     scaledY = (long)y * (long)kNewScreenY / kOriginalScreenY;
  456.  
  457.     LineToOffscreen(scaledX, scaledY);
  458. }
  459.  
  460.  
  461. /*
  462.     SetLineColor
  463.     
  464.     This function sets the value of the global gPixelColor 
  465.     to the appropriate value for the give color.
  466. */
  467. void SetLineColor(short colorCode)
  468. {
  469.     RGBColor        curColor;
  470.  
  471.     ForeColor(colorCode);
  472.     GetForeColor(&curColor);
  473.  
  474.     gPixelColor = Color2Index(&curColor);
  475. }
  476.  
  477.  
  478. /*
  479.     MoveToOffscreen
  480.     
  481.     This function replaces the MoveTo function and is used
  482.     for off-screen drawing.
  483. */
  484. void MoveToOffscreen(short x, short y)
  485. {
  486.     gCurrentX = x;
  487.     gCurrentY = y;
  488. }
  489.  
  490.  
  491. /*
  492.     LineToOffscreen
  493.     
  494.     This function replaces the LineTo function with a custom
  495.     off-screen line drawing function.
  496. */
  497. void LineToOffscreen(short x, short y)
  498. {
  499.     if (abs(y - gCurrentY) > abs(x - gCurrentX)) {
  500.         if (y < gCurrentY)
  501.             LineYOffscreen(x, y, gCurrentX, gCurrentY);
  502.         else
  503.             LineYOffscreen(gCurrentX, gCurrentY, x, y);
  504.     }
  505.     else {
  506.         if (x < gCurrentX)
  507.             LineXOffscreen(x, y, gCurrentX, gCurrentY);
  508.         else
  509.             LineXOffscreen(gCurrentX, gCurrentY, x, y);
  510.     }
  511.     
  512.     gCurrentX = x;
  513.     gCurrentY = y;
  514. }
  515.  
  516.  
  517. /*
  518.     LineXOffscreen
  519.  
  520.     Draws lines with a slope between -1 and 1
  521. */
  522. void LineXOffscreen(short startX, short startY, short endX, short endY)
  523. {
  524.     short            currentX;
  525.     float            slope, currentY, newY;
  526.     float            yRowIncrement;
  527.     char*            pixelAddress;
  528.     long            deltaY;
  529.     
  530.     if (endX == startX)
  531.         slope = 0;
  532.     else
  533.         slope = (float)(endY - startY) / (float)(endX - startX);
  534.  
  535.     pixelAddress = gPixelBase + startX;
  536.     yRowIncrement = (short)((*gOffscreenPixMap)->rowBytes & 0x7FFF);
  537.     pixelAddress += (unsigned long)(startY * yRowIncrement);
  538.     currentY = startY + 0.5;
  539.     
  540.     for (currentX = startX; currentX <= endX; currentX++) {
  541.         *pixelAddress++ = gPixelColor;
  542.         newY = currentY + slope;
  543.         pixelAddress += (long)(yRowIncrement * ((long)(newY) - (long)(currentY)));
  544.         currentY = newY;
  545.     }
  546. }
  547.  
  548.  
  549. /*
  550.     LineYOffscreen
  551.  
  552.     Draws lines with a slope <= -1 or >= 1
  553. */
  554. void LineYOffscreen(short startX, short startY, short endX, short endY)
  555. {
  556.     short            currentY;
  557.     float            slope, currentX, newX;
  558.     unsigned long    yRowIncrement;
  559.     char*            pixelAddress;
  560.     
  561.     if (endY == startY)
  562.         slope = 0;
  563.     else
  564.         slope = (float)(endX - startX) / (float)(endY - startY);
  565.  
  566.     pixelAddress = gPixelBase + startX;
  567.     yRowIncrement = (*gOffscreenPixMap)->rowBytes & 0x7FFF;
  568.     pixelAddress += (unsigned long)(startY * yRowIncrement);
  569.     currentX = startX + 0.5;
  570.     
  571.     for (currentY = startY; currentY <= endY; currentY++) {
  572.         *pixelAddress = gPixelColor;
  573.         newX = currentX + slope;
  574.         pixelAddress += yRowIncrement + (long)(newX) - (long)(currentX);
  575.         currentX = newX;
  576.     }
  577. }
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585.